#-----------------------------------
# VisualK-OS                        
#    Module: Gamepad Manager [v2.1] 
#    Author: Kevin Slaughter        
#-----------------------------------

"""
[Revision History]
    v2.1: Global debug output simplification.
    v2.0: Standardization, misc. optimizations.
    v1.0: Initial implementation.
"""



### Lib/Header import
from VKCC_Constants import *
import globals, pygame

### Obj: Gamepad
class cGamePad(object):
    # Properties
    ### [General]
    isAvailable = False		# Status indicator
    curMap = 0			# Current button map index
    sPath="/dev/input/js0"	# Path to joystick interface
    numAxes=0; numButtons=0     # Counts for polling states
    numJoysticks = 0            # Number of detected joysticks. We only ever use 1... For now.
    momentary = True		# Lights reflect button state (down = on | up = off)
    aS=dict({'0':0})		# Axis states
    bS=dict({'0':0})		# Button states


    # Methods
    ### buttonToStr(): Resolves button number to name/symbol
    def buttonToStr(self, b):
        if (b == B_SQUARE):         return "Square"
        elif (b == B_TRIANGLE):     return "Triangle"
        elif (b == B_CIRCLE):       return "Circle"
        elif (b == B_X):            return "X"
        elif (b == B_UP):           return "Up"
        elif (b == B_RIGHT):        return "Right"
        elif (b == B_DOWN):         return "Down"
        elif (b == B_LEFT):         return "Left"
        elif (b == B_L1):           return "L1"
        elif (b == B_L2):           return "L2"
        elif (b == B_L3):           return "L3"
        elif (b == B_R1):           return "R1"
        elif (b == B_R2):           return "R2"
        elif (b == B_R3):           return "R3"
        elif (b == B_SELECT):       return "Select"
        elif (b == B_START):        return "Start"
        return
    
    
    ### initGP(): Initialize joystick subsystem
    def initGP(self):
	# Debug mode output
        globals.dbg("[*] Initializing GamePad I/O subsystem...")
        
        # Initialize and enumerate joysticks/gamepads
        pygame.init(); pygame.joystick.init()
        self.numJoysticks = pygame.joystick.get_count()
        if (self.numJoysticks==0):
            self.isAvailable = False
            print("   - Detected zero gamepads.\n     [!] GamePad functions disabled.\n")
        else:
            print("[*] Detected "+str(self.numJoysticks)+" gamepads.\n")
            self.gpad = pygame.joystick.Joystick(0); self.gpad.init()
            
	    # Set up internal button/axis state variables
            self.numAxes = self.gpad.get_numaxes()
            self.numButtons = self.gpad.get_numbuttons()
            self.isAvailable = True
            
            # Define internal button state arrays
            for i in range(0,self.numAxes):     self.aS[str(i)] = 0;
            for i in range(0,self.numButtons):  self.bS[str(i)] = 0;
        return



	### [Button state array]
	# Effect ideas;
	# 1.) L3/R3 buttons set board to strobe mode while held down, y axis determines strobe frequency.
	# 2.) Holding shoulder triggers affects fade-in/out time of lights (default = 500ms)
	#     - (Fade in mod)  L1 = 1000ms | L2 = 2000ms
	#     - (Fade out mod) R1 = 1000ms | R2 = 2000ms



    ### [Functions]
    ### closeGP(); Gracefully exit PyGame processes
    def closeGP(self):
	globals.dbg("[*] Closing gamepad interface...")
	pygame.quit()
	return


    ### setMapping(): Sets active controller mapping
    def setMapping(self, iMap):
        # TODO: Set button macros accordingly
        self.curMap = iMap
	globals.dbg("[*] Changed gamepad mapping to #"+iMap+"...")
        return


    ### handleNonMomentary(): Handle non-momentary light triggering (state mgmt)
    def handleNonMomentary(self, iB, iL, iC=0):
        if(self.bS[iB]==1):	    # Light is on
            self.bS[iB] = 0
            globals.CB.SendCmd(globals.FBA,CB.CMD_LIGHT_OFF,iL)	    # Turn OFF
	else:			    # Light is off
            self.bS[iB] = 1
            globals.CB.SendCmd(globals.FBA,CB.CMD_LIGHT_ON,iL,iC)   # Turn ON
        return


    ### buttonPressed(): Process button event
    def buttonPressed(self, button):
        ### Base setup
        sB = str(button); iLight = 0; iColor = VKC_GREEN#; iCmd = CB.CMD_NONE
        iParam = 0
        
        ### Parse button presses
	# Start
        if(button == B_START):		### START - Toggle momentary lighting
            self.bS[sB] = 1; self.momentary = (not self.momentary)
            globals.dbg("[*] Momentary Lights ==  "+str(self.momentary))
        # Select
        if(button == B_SELECT):		### SELECT - Change button map
            if(self.curMap < 2): self.curMap += 1
            elif(self.curMap >= 2): self.curMap = 0
            
            # String resolution of mapping mode
            sDesc = "[untitled mapping]"
            if(self.curMap == 0): sDesc = "[Full-board color floods]"
            elif(self.curMap == 1): sDesc = "[Individual lamps]"
            
            # Notification to operator
            globals.dbg("[*] Button map is now #"+str(self.curMap)+" ["+sDesc+"]")

        # Symbol buttons
        if(button == B_TRIANGLE):	# TRIANGLE
            if(self.curMap == 0):
                iCmd = CMD_ALL_ON; iColor = 1; iParam = VKC_RED
        
        if(button == B_CIRCLE):		# CIRCLE
            if(self.curMap == 0):
                iCmd = CMD_ALL_ON; iColor = 1; iParam = VKC_GREEN
            
        if(button == B_X):		# X
            if(self.curMap == 0):
                iCmd = CMD_ALL_ON; iColor = 1; iParam = VKC_BLUE
            
        if(button == B_SQUARE):		# SQUARE
            if(self.curMap == 0):
                iCmd = CMD_ALL_ON; iColor = 1; iParam = VKC_ORANGE
        
        ### Directional buttons
        if(button == B_UP):		# D-PAD: UP
            if(self.curMap == 0):
                iCmd = CMD_ALL_ON; iColor = 1; iParam = VKC_RED
        
        if(button == B_RIGHT):		# D-PAD: DOWN
            if(self.curMap == 0):
                iCmd = CMD_ALL_ON; iColor = 0; iParam = VKC_GREEN
                        
        if(button == B_DOWN):		# D-PAD: DOWN
            if(self.curMap == 0):
                iCmd = CMD_ALL_ON; iColor = 0; iParam = VKC_BLUE
            
        if(button == B_LEFT):		# D-PAD: LEFT
            if(self.curMap == 0):
                iCmd = CMD_ALL_ON; iColor = 0; iParam = VKC_ORANGE
            
        ### Shoulder buttons
        if(button == B_L1):		# L1
            iLight = 1		# Board #0
            iCmd = CMD_COLOR_WALL_ON;
        
        if(button == B_L2):		# L2
            iLight = 1		# Board #1
            iCmd = CMD_COLOR_WALL_OFF;
        
        # Send cmd
        #if(self.momentary): CB.sendCmd(globals.FBA, iCmd, iColor, iParam)
        #else: self.handleNonMomentary(sB, iLight, iParam)
    
    
    ### buttonReleased(): Turn off light assigned to x-button
    def buttonReleased(self, button):
        iL =0   # Target light board
        if(button == B_UP): iL = 0
        if(button == B_RIGHT): iL = 0
        if(button == B_DOWN): iL = 0
        if(button == B_LEFT): iL = 0

        if(button == B_TRIANGLE): iL = 1
        if(button == B_CIRCLE): iL = 1
        if(button == B_X): iL = 1
        if(button == B_SQUARE): iL = 1

        # Send command string
        #globals.CB.sendCmd(FBA, CMD_ALL_OFF, iL, 0);
        
        
    ### handleButtonEvents(): Process gamepad events. This is called to process the
    ###		     lighting events, independent of screen painting and updating...
    ###          eg: Pseudo-background process.
    def handleEvents(self):
        for event in pygame.event.get():
            ### Button released
            if (event.type == pygame.JOYBUTTONUP):
                print("Button "+str(event.button)+" released")
                self.buttonReleased(event.button)
            ### Button pressed
            if (event.type == pygame.JOYBUTTONDOWN):
                print("Button "+str(event.button)+" pressed")
                self.buttonPressed(event.button)
        return
